package com.superman.router

import com.google.devtools.ksp.getConstructors
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.google.devtools.ksp.validate
import java.io.OutputStream

fun OutputStream.appendText(str: String) {
    this.write(str.toByteArray())
}

class BuilderProcessor(
    val codeGenerator: CodeGenerator,
    val logger: KSPLogger
) : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        val symbols = resolver.getSymbolsWithAnnotation(RouteClassName::class.qualifiedName!!)
        val ret = symbols.filter { !it.validate() }.toList()
        symbols.filter { it is KSClassDeclaration && it.validate() }
            .forEach { it.accept(BuilderVisitor(), Unit) }
        return ret
    }

    inner class BuilderVisitor : KSVisitorVoid() {

        override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {

            var inputSimpleName = "KspError"
            var inputPackageName = "KspErrorPackage"

            var constructP1: KSType? = null
            var constructP2: KSType? = null

            val classDeclarationName =
                classDeclaration.packageName.asString() + "." + classDeclaration.simpleName.asString()
            if (classDeclaration.superTypes.count() != 1) {
                logger.error("$classDeclarationName 必须实现且仅实现一个接口")
            }

            val clazz = classDeclaration.superTypes.elementAt(0).resolve()

            classDeclaration.annotations.forEach { anno ->
                if (anno.shortName.asString() == RouteClassName::class.java.simpleName) {
                    inputPackageName = clazz.declaration.packageName.asString()
                    inputSimpleName = clazz.declaration.simpleName.asString()
                }
            }
            val construct = classDeclaration.getConstructors().elementAt(0)
            if (construct.parameters.size == 2) {
                constructP1 = construct.parameters[0].type.resolve()
                constructP2 = construct.parameters[1].type.resolve()
            }


            val className = "${inputSimpleName}Builder"
            val qClassName = classDeclaration.qualifiedName!!.asString()
            val file = codeGenerator.createNewFile(
                Dependencies(true, classDeclaration.containingFile!!),
                inputPackageName, className
            )
            file.appendText("package $inputPackageName\n\n")
            if (constructP1 != null && constructP2 != null) {
                val p1Name = constructP1.declaration.qualifiedName!!.asString()
                val p2Name = constructP2.declaration.qualifiedName!!.asString()
                file.appendText("class $className : ${InstanceFactory2Params::class.qualifiedName}<$inputPackageName.$inputSimpleName,$p1Name,$p2Name> {\n\n")
                file.appendText("  override fun construct(p1:$p1Name,p2:$p2Name):$inputPackageName.$inputSimpleName {\n\n")
                file.appendText("    return $qClassName(p1,p2)\n\n")
                file.appendText("  }\n\n")
            } else {
                file.appendText("class $className : ${InstanceFactory::class.qualifiedName}<$inputPackageName.$inputSimpleName> {\n\n")
                file.appendText("  override fun get():$inputPackageName.$inputSimpleName {\n\n")
                file.appendText("    return $qClassName()\n\n")
                file.appendText("  }\n\n")
            }

            file.appendText("}\n\n")
            file.close()
        }

    }
}


class BuilderProcessorProvider : SymbolProcessorProvider {
    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
        return BuilderProcessor(environment.codeGenerator, environment.logger)
    }

}